from message import *
import random

class Card:
	color1 = 'A'
	color2 =  'B'
	color3 =  'C'
	color4 =  'D'
	colors = [color1, color2, color3, color4]
	skip =  'S'
	reverse =  'R'
	wild = 'J'
	wildfour = '+4'
	drawtwo = '+2'
	
	def __init__(self, str):
		self.str = Card.sto_str(str)
		self.reset()
	
	def reset (self):
		self.color = 'E'
		if self.isSpecial():
			self.pause = True
		else:
			self.pause = False
	
	def isColour (self):
		return not self.isSpecial()
	
	def isWaiting (self):
		return self.pause
	
	def setColor (self, color):
		self.color = color
		self.pause = False
	
	def wait (self):
		self.pause = True
	
	def sameFigure (self, other):
		other = self.to_card(other)
		
		if self.isDrawTwo() or other.isDrawTwo():
			return self.isDrawTwo() and other.isDrawTwo()
		
		if self.isSpecial() or other.isSpecial():
			return self.str == other.str
			
		if self.isColour():
			return self.str[1] == other.str[1]
		
		return false
		
	def sameColour (self, other):
		str2 = Card.sto_str(other)
		return self.str[0] == str2[0] or self.color == str2[0]
	
	def isSpecial (self):
		return self.isWildFour() or self.isWild()
	
	def isWildFour (self):
		return self.str == self.__class__.wildfour
	
	def isDrawTwo (self):
		return self.str[1:3] == self.__class__.drawtwo
	
	def isWild (self):
		return self.str == self.__class__.wild
	
	def isSkip (self):
		return self.str[1] == self.__class__.skip
	
	def isReverse (self):
		return self.str[1] == self.__class__.reverse
	
	def __cmp__(self, other):
		str2 = Card.sto_str(other)
		return cmp(self.str, str2)
	
	def equals (self, other):
		str2 = Card.sto_str(other)
		return self.str == str2
		
	def to_str(self):
		return self.str
	
	def to_card(self, obj):
		if isinstance(obj, Card) : return obj
		return Card(obj)
		
	def __repr__(self):
		return self.str
	
	@staticmethod
	def sto_str(obj):
		if isinstance(obj, Card) : return obj.str
		return obj

class Deck:
	def __init__(self, content = None):
		if content == None:
			deck = []
			#~ for i in [Card.color1, Card.color2, Card.color3, Card.color4]:
			for i in Card.colors:
				deck.append('%s0' % (i))
				for k in range(2):
					for j in range(1, 10) + [Card.skip, Card.reverse, Card.drawtwo]:
						deck.append('%s%s' % (i, j))
			
			for i in [Card.wild, Card.wildfour]:
				for j in range(4):
					deck.append('%s' % (i))
			
			self.content = []
			for c in deck:
				self.content.append(Card(c))
		else:
			self.content = content
			
	def shuffle(self):
		olddeck = self.content
		newdeck = []
		while olddeck != []:
			i = random.randint(1, len(olddeck))
			card = olddeck[i - 1]
			card.reset()
			newdeck.append(card)
			olddeck[i - 1:i] = []
		
		self.content = newdeck
	
	def draw(self, count = 1):
		res = self.content[0:count]
		self.content[0:count] = []
		return res
	
	def discard(self, card):
		self.content[:0] = [card]
	
	def pop_top(self):
		return self.draw()
	
	def __getattr__ (self, name):
		if name == 'top':
			return self.content[0]


class Player:
	def __init__(self):
		self.game = None
		self.cards = []
		
	def draw(self, params = {}):
		self.cards.extend(self.game.draw(self))
		
	def play(self, params):
		card = Card(params['card'])
#		print self.cards.index(card)
		
		try:
			self.cards.index(card)
		except:
			raise Exception('[Card %s] not found in player hand.' % (card)) 
		
		if self.game.play(card, self):
			self.cards.remove(card)
		else:
			self.cards.extend(self.game.draw_penalty(self, 2))
		
	def __getattr__ (self, name):
		if name == 'nb_cards':
			return len(self.cards)

class GameFactory:
	def __init__(self):
		pass


class Game:
	def __init__(self):
		self.players = []
		self.nb_begin = 7
		self.stock = Deck()
		self.discard = Deck([])
		self.log = MessageLog()
		self.clockwise = 1
		self.inc_value = 1
		self.initializing = False
	
	def start(self, params = {}):
		self.initializing = True
		if not params.has_key('turn'): params['turn'] = 0
		
		self.log.broadcast('Game start')
		self.turn = params['turn']
		self.counter = 0
		if not params.has_key('shuffle') or params['shuffle'] == 1:
			self.stock.shuffle()
		for i in range(self.nb_begin):
			for p in self.players:
				p.draw()
		self.discard.discard(self.stock.draw()[0])
		
		self.log.broadcast('First card is [%s]' % (self.discard.top))
		self.next_turn()
		self.initializing = False
	
	def add_player(self, player):
		player.position = len(self.players)
		player.id = player.position
		self.players += [player]
		player.game = self
	
	def next_turn(self):
		self.turn = (self.turn + (self.clockwise * self.inc_value)) % len(self.players)
		self.inc_value = 1
		
	def get_turn_play(self, card):
		return self.players[self.turn]
	
	def __getattr__ (self, name):
		if name == 'current_player':
			return self.players[self.turn]
	
	
	def choose_color (self, player, color):
		if player.position == self.last_player.position:
			self.discard.top.setColor(color)
			self.log.broadcast('[Player %s] choose color [%s].' % (player.position, color))
	
	def play(self, card, player):
		top = self.discard.top
		ok = False
		
		#print ('[%s] [%s] [%s] [%s] [%s]' % (ok, card, top, card.equals(top), card.str == top.str))
		
		if card.equals(top):
			ok = True
		elif self.current_player.position == player.position:
			if top.isWaiting():
				pass
			elif self.counter > 0:
				if top.isWildFour():
					if card.isWild() : ok = True
				elif top.isDrawTwo():
					if card.isWild() : ok = True
					if card.isDrawTwo() : ok = True
			else:
				if card.isSpecial():
					ok = True
				elif top.sameColour(card) or top.sameFigure(card):
					ok = True
			
		if ok:
			self.last_player = player
			
			if card.isWildFour():
				self.counter += 4
			elif card.isDrawTwo():
				self.counter += 2
			elif card.isWild():
				self.counter = 0
			elif card.isReverse():
				self.clockwise *= -1
			elif card.isSkip():
				self.inc_value = 2
			
			if self.current_player.position != player.position:
				self.log.broadcast('[Player %s] owned [player %s] on his turn and played [%s].' % (player.position, self.current_player.position, card))
				self.turn = player.position
			else:
				self.log.broadcast('[Player %s] played [%s].' % (self.current_player.position, card))
			
			if card.isSpecial():
				self.log.broadcast('Waiting for [Player %s] to choose a color.' % (player.position))
			
			self.discard.discard(card)
			self.next_turn()
			
		elif self.current_player.position == player.position:
			self.log.broadcast('[Player %s] got a additional penality of [2 x cards] on trying to play [%s].' % (player.position, card))
			self.draw(self, player, 1 + self.counter * 1 + 2)
			
		else:
			self.log.broadcast('[Player %s] tried to play [%s] but fails.' % (player.position, card))
		
		return ok
		
	def draw_penalty(self, player, count):
		return self.draw_count(count)
		
	def draw_count(self, count):
		stock_size = len(self.stock.content)
		if stock_size < count:
			res = self.stock.draw(stock_size)
			top = self.discard.pop_top()
			self.stock = self.discard
			self.stock.shuffle()
			self.discard = Deck([top])
			
			res.extend(self.stock.draw(count - stock_size))
			self.next_turn()
			return res
			
		return self.stock.draw(count)
		
	def draw(self, player, count = None):
		if count == None: count = 1 + self.counter * 1
		self.counter = 0
		if not self.initializing: self.log.broadcast('[Player %s] draw [%s x cards].' % (player.position, count))
		cards = self.draw_count(count)
		
		if self.current_player.position == player.position:
			self.next_turn()
		
		return cards
	
	